package com.shy.pointcut;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import com.shy.pointcut.LogPointcut.RequestErrorInfo;
import com.shy.pointcut.LogPointcut.RequestInfo;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
@Component
public class LogPointcut {
	@Pointcut("execution(public * com.shy.controller.*.*(..))")
	public void requestServer() {
	}

	public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		long start = System.currentTimeMillis();
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		Object result = proceedingJoinPoint.proceed();
		RequestInfo requestInfo = new RequestInfo();
		requestInfo.setIp(request.getRemoteAddr());
		requestInfo.setUrl(request.getRequestURI().toString());
		requestInfo.setHttpMethod(request.getMethod());
		requestInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
					proceedingJoinPoint.getSignature().getName()));
		requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
		requestInfo.setResult(result);
		requestInfo.setTimeCost(System.currentTimeMillis() - start);
		log.info("Request Info 			: {}", requestInfo);
		return result;
	}

	@AfterThrowing(pointcut = "requestServer()", throwing = "e")
	public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
		ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		RequestErrorInfo requestErrorInfo = new RequestErrorInfo();
		requestErrorInfo.setIp(request.getRemoteAddr());
		requestErrorInfo.setUrl(request.getRequestURL().toString());
		requestErrorInfo.setHttpMethod(request.getMethod());
		requestErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
		joinPoint.getSignature().getName()));
		requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
		requestErrorInfo.setException(e);
		log.info("Error Request Info      : {}", requestErrorInfo);
	}

	/**
	 * 获取入参
	 * 
	 * @param proceedingJoinPoint
	 */
	private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
		// 参数名
		String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
		// 参数值
		Object[] paramValues = proceedingJoinPoint.getArgs();
		return buildRequestParam(paramNames, paramValues);
	}

	private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
		// 参数名
		String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
		// 参数值
		Object[] paramValues = joinPoint.getArgs();
		return buildRequestParam(paramNames, paramValues);
	}

	private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
		Map<String, Object> requestParams = new HashMap<>(paramNames.length);
		for (int i = 0; i < paramNames.length; i++) {
			Object value = paramValues[i];
			// 如果是文件对象
			if (value instanceof MultipartFile) {
				MultipartFile file = (MultipartFile) value;
				value = file.getOriginalFilename(); // 获取文件名
			}
			requestParams.put(paramNames[i], value);
		}
		return requestParams;
	}

	@Data
	public class RequestInfo implements Serializable {
		private String ip; // ip地址
		private String url; // 请求路径
		private String httpMethod; // 方法类型
		private String classMethod; // 类和方法名称
		private Object requestParams; // 请求参数
		private Object result; // 返回的结果
		private Long timeCost; // 执行时长
	}

	@Data
	public class RequestErrorInfo implements Serializable {
		private String ip;
		private String url;
		private String httpMethod;
		private String classMethod;
		private Object requestParams;
		private RuntimeException exception; // 异常类型
	}
}
